home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2004 April / Gamestar_61_2004-04_dvdb.iso / DVDStar / Editace / hltp.exe / {app} / Source Code / Zoners Half-Life Tools / hlcsg / textures.cpp < prev    next >
C/C++ Source or Header  |  2002-11-10  |  24KB  |  781 lines

  1. #include "csg.h"
  2.  
  3. #define MAXWADNAME 16
  4. #define MAX_TEXFILES 128
  5.  
  6. //  FindMiptex
  7. //  TEX_InitFromWad
  8. //  FindTexture
  9. //  LoadLump
  10. //  AddAnimatingTextures
  11.  
  12.  
  13. typedef struct
  14. {
  15.     char            identification[4];                     // should be WAD2/WAD3
  16.     int             numlumps;
  17.     int             infotableofs;
  18. } wadinfo_t;
  19.  
  20. typedef struct
  21. {
  22.     int             filepos;
  23.     int             disksize;
  24.     int             size;                                  // uncompressed
  25.     char            type;
  26.     char            compression;
  27.     char            pad1, pad2;
  28.     char            name[MAXWADNAME];                      // must be null terminated
  29.  
  30.     int             iTexFile;                              // index of the wad this texture is located in
  31.  
  32. } lumpinfo_t;
  33.  
  34. std::deque< std::string > g_WadInclude;
  35. std::map< int, bool > s_WadIncludeMap;
  36.  
  37. static int      nummiptex = 0;
  38. static lumpinfo_t miptex[MAX_MAP_TEXTURES];
  39. static int      nTexLumps = 0;
  40. static lumpinfo_t* lumpinfo = NULL;
  41. static int      nTexFiles = 0;
  42. static FILE*    texfiles[MAX_TEXFILES];
  43.  
  44. // fix for 64 bit machines
  45. #if SIZEOF_CHARP == 8
  46.     static char* texmap64[MAX_MAP_BRUSHES];
  47.     static int   tex_max64=0;
  48.  
  49.     static inline int texmap64_store(char *texname)
  50.     {
  51.         int curr_tex;
  52.         ThreadLock();
  53.         if (tex_max64 >= MAX_MAP_BRUSHES)   // no assert?
  54.         {
  55.             printf("MAX_MAP_BRUSHES exceeded!\n");
  56.             exit(-1);
  57.         }
  58.         curr_tex = tex_max64;
  59.         texmap64[tex_max64] = texname;
  60.         tex_max64++;
  61.         ThreadUnlock();
  62.         return curr_tex;
  63.     }
  64.  
  65.     static inline char* texmap64_retrieve( int index)
  66.     {
  67.         if(index > tex_max64)
  68.         {
  69.             printf("retrieving bogus texture index %d\n", index);
  70.             exit(-1);
  71.         }
  72.         return texmap64[index];
  73.     }
  74.  
  75. #else // SIZEOF_CHARP != 8, almost certainly 4
  76.     #define texmap64_store( A ) ( (int) A)
  77.     #define texmap64_retrieve( A ) ( (char*) A)
  78. #endif // SIZEOF_CHARP
  79.  
  80. // =====================================================================================
  81. //  CleanupName
  82. // =====================================================================================
  83. static void     CleanupName(const char* const in, char* out)
  84. {
  85.     int             i;
  86.  
  87.     for (i = 0; i < MAXWADNAME; i++)
  88.     {
  89.         if (!in[i])
  90.         {
  91.             break;
  92.         }
  93.  
  94.         out[i] = toupper(in[i]);
  95.     }
  96.  
  97.     for (; i < MAXWADNAME; i++)
  98.     {
  99.         out[i] = 0;
  100.     }
  101. }
  102.  
  103. // =====================================================================================
  104. //  lump_sorters
  105. // =====================================================================================
  106.  
  107. static int CDECL lump_sorter_by_wad_and_name(const void* lump1, const void* lump2)
  108. {
  109.     lumpinfo_t*     plump1 = (lumpinfo_t*)lump1;
  110.     lumpinfo_t*     plump2 = (lumpinfo_t*)lump2;
  111.  
  112.     if (plump1->iTexFile == plump2->iTexFile)
  113.     {
  114.         return strcmp(plump1->name, plump2->name);
  115.     }
  116.     else
  117.     {
  118.         return plump1->iTexFile - plump2->iTexFile;
  119.     }
  120. }
  121.  
  122. static int CDECL lump_sorter_by_name(const void* lump1, const void* lump2)
  123. {
  124.     lumpinfo_t*     plump1 = (lumpinfo_t*)lump1;
  125.     lumpinfo_t*     plump2 = (lumpinfo_t*)lump2;
  126.  
  127.     return strcmp(plump1->name, plump2->name);
  128. }
  129.  
  130. // =====================================================================================
  131. //  FindMiptex
  132. //      Find and allocate a texture into the lump data
  133. // =====================================================================================
  134. static int      FindMiptex(const char* const name)
  135. {
  136.     int             i;
  137.  
  138.     ThreadLock();
  139.     for (i = 0; i < nummiptex; i++)
  140.     {
  141.         if (!strcmp(name, miptex[i].name))
  142.         {
  143.             ThreadUnlock();
  144.             return i;
  145.         }
  146.     }
  147.  
  148.     hlassume(nummiptex < MAX_MAP_TEXTURES, assume_MAX_MAP_TEXTURES);
  149.     safe_strncpy(miptex[i].name, name, MAXWADNAME);
  150.     nummiptex++;
  151.     ThreadUnlock();
  152.     return i;
  153. }
  154.  
  155. // =====================================================================================
  156. //  TEX_InitFromWad
  157. // =====================================================================================
  158. bool            TEX_InitFromWad()
  159. {
  160.     int             i, j;
  161.     wadinfo_t       wadinfo;
  162.     char            szTmpWad[1024]; // arbitrary, but needs to be large.
  163.     char*           pszWadFile;
  164.     const char*     pszWadroot;
  165.     wadpath_t*      currentwad;
  166.  
  167.     Log("\n"); // looks cleaner
  168.  
  169.     szTmpWad[0] = 0;
  170.     pszWadroot = getenv("WADROOT");
  171.  
  172. #ifdef HLCSG_AUTOWAD
  173.     autowad_UpdateUsedWads();
  174. #endif
  175.  
  176.     // for eachwadpath
  177.     for (i = 0; i < g_iNumWadPaths; i++)
  178.     {
  179.         FILE*           texfile;                           // temporary used in this loop
  180.         bool            bExcludeThisWad = false;
  181.  
  182.         currentwad = g_pWadPaths[i];
  183.         pszWadFile = currentwad->path;        
  184.  
  185.         
  186. #ifdef HLCSG_AUTOWAD
  187.         #ifdef _DEBUG
  188.         Log("[dbg] Attempting to parse wad: '%s'\n", pszWadFile);
  189.         #endif
  190.  
  191.         if (g_bWadAutoDetect && !currentwad->usedtextures)
  192.             continue;
  193.  
  194.         #ifdef _DEBUG
  195.         Log("[dbg] Parsing wad\n");
  196.         #endif
  197. #endif
  198.  
  199.         texfiles[nTexFiles] = fopen(pszWadFile, "rb");
  200.  
  201.         #ifdef SYSTEM_WIN32
  202.         if (!texfiles[nTexFiles])
  203.         {
  204.             // cant find it, maybe this wad file has a hard code drive
  205.             if (pszWadFile[1] == ':')
  206.             {
  207.                 pszWadFile += 2;                           // skip past the drive
  208.                 texfiles[nTexFiles] = fopen(pszWadFile, "rb");
  209.             }
  210.         }
  211.         #endif
  212.  
  213.         if (!texfiles[nTexFiles] && pszWadroot)
  214.         {
  215.             char            szTmp[_MAX_PATH];
  216.             char            szFile[_MAX_PATH];
  217.             char            szSubdir[_MAX_PATH];
  218.  
  219.             ExtractFile(pszWadFile, szFile);
  220.             
  221.             ExtractFilePath(pszWadFile, szTmp);
  222.             ExtractFile(szTmp, szSubdir);
  223.  
  224.             // szSubdir will have a trailing separator
  225.             safe_snprintf(szTmp, _MAX_PATH, "%s" SYSTEM_SLASH_STR "%s%s", pszWadroot, szSubdir, szFile);
  226.             texfiles[nTexFiles] = fopen(szTmp, "rb");
  227.             
  228.             #ifdef SYSTEM_POSIX
  229.             if (!texfiles[nTexFiles])
  230.             {
  231.                 // if we cant find it, Convert to lower case and try again
  232.                 strlwr(szTmp);
  233.                 texfiles[nTexFiles] = fopen(szTmp, "rb");
  234.             }
  235.             #endif
  236.         }
  237.  
  238.         if (!texfiles[nTexFiles])
  239.         {
  240.             // still cant find it, error out
  241.             Fatal(assume_COULD_NOT_FIND_WAD, "Could not open wad file %s", pszWadFile);
  242.             continue;
  243.         }
  244.  
  245.         // look and see if we're supposed to include the textures from this WAD in the bsp.
  246.         WadInclude_i it;
  247.         for (it = g_WadInclude.begin(); it != g_WadInclude.end(); it++)
  248.         {
  249.             if (stristr(pszWadFile, it->c_str()))
  250.             {
  251.                 Log("Including Wadfile: %s\n", pszWadFile);
  252.                 bExcludeThisWad = true;             // wadincluding this one
  253.                 s_WadIncludeMap[nTexFiles] = true;
  254.                 break;
  255.             }
  256.         }
  257.  
  258.         if (!bExcludeThisWad)
  259.         {
  260.             Log("Using Wadfile: %s\n", pszWadFile);
  261.             safe_snprintf(szTmpWad, 1024, "%s%s;", szTmpWad, pszWadFile);
  262.         }
  263.  
  264.         // temp assignment to make things cleaner:
  265.         texfile = texfiles[nTexFiles];
  266.  
  267.         // read in this wadfiles information
  268.         SafeRead(texfile, &wadinfo, sizeof(wadinfo));
  269.  
  270.         // make sure its a valid format
  271.         if (strncmp(wadinfo.identification, "WAD2", 4) && strncmp(wadinfo.identification, "WAD3", 4))
  272.         {
  273.             Log(" - ");
  274.             Error("%s isn't a Wadfile!", pszWadFile);
  275.         }
  276.  
  277.         wadinfo.numlumps        = LittleLong(wadinfo.numlumps);
  278.         wadinfo.infotableofs    = LittleLong(wadinfo.infotableofs);
  279.  
  280.         // read in lump
  281.         if (fseek(texfile, wadinfo.infotableofs, SEEK_SET))
  282.             Warning("fseek to %d in wadfile %s failed\n", wadinfo.infotableofs, pszWadFile);
  283.  
  284.         // memalloc for this lump
  285.         lumpinfo = (lumpinfo_t*)realloc(lumpinfo, (nTexLumps + wadinfo.numlumps) * sizeof(lumpinfo_t));
  286.  
  287.         // for each texlump
  288.         for (j = 0; j < wadinfo.numlumps; j++, nTexLumps++)
  289.         {
  290.             SafeRead(texfile, &lumpinfo[nTexLumps], (sizeof(lumpinfo_t) - sizeof(int)) );  // iTexFile is NOT read from file
  291.  
  292.             if (!TerminatedString(lumpinfo[nTexLumps].name, MAXWADNAME))
  293.             {
  294.                 lumpinfo[nTexLumps].name[MAXWADNAME - 1] = 0;
  295.                 Log(" - ");
  296.                 Warning("Unterminated texture name : wad[%s] texture[%d] name[%s]\n", pszWadFile, nTexLumps, lumpinfo[nTexLumps].name);
  297.             }
  298.  
  299.             CleanupName(lumpinfo[nTexLumps].name, lumpinfo[nTexLumps].name);
  300.  
  301.             lumpinfo[nTexLumps].filepos = LittleLong(lumpinfo[nTexLumps].filepos);
  302.             lumpinfo[nTexLumps].disksize = LittleLong(lumpinfo[nTexLumps].disksize);
  303.             lumpinfo[nTexLumps].iTexFile = nTexFiles;
  304.             
  305.             if (lumpinfo[nTexLumps].disksize > MAX_TEXTURE_SIZE)
  306.             {
  307.                 Log(" - ");
  308.                 Warning("Larger than expected texture (%d bytes): '%s'", 
  309.                     lumpinfo[nTexLumps].disksize, lumpinfo[nTexLumps].name);
  310.             }
  311.  
  312.         }
  313.  
  314.         // AJM: this feature is dependant on autowad. :(
  315.         // CONSIDER: making it standard?
  316. #ifdef HLCSG_AUTOWAD
  317.         {
  318.             double percused = ((float)(currentwad->usedtextures) / (float)(g_numUsedTextures)) * 100;
  319.             Log(" - Contains %i used texture%s, %2.2f percent of map (%d textures in wad)\n", 
  320.                 currentwad->usedtextures, currentwad->usedtextures == 1 ? "" : "s", percused, wadinfo.numlumps);
  321.         }
  322. #endif
  323.  
  324.         nTexFiles++;
  325.         hlassume(nTexFiles < MAX_TEXFILES, assume_MAX_TEXFILES);
  326.     }
  327.  
  328.     //Log("num of used textures: %i\n", g_numUsedTextures);
  329.  
  330.     // AJM: Tommy suggested i add this warning message in, and  it certianly doesnt 
  331.     //  hurt to be cautious. Especially one of the possible side effects he mentioned was svc_bad
  332.     if (nTexFiles > 8) 
  333.     {
  334.         Log("\n");
  335.         Warning("More than 8 wadfiles are in use. (%i)\n"
  336.                 "This may be harmless, and if no strange side effects are occurring, then\n"
  337.                 "it can safely be ignored. However, if your map starts exhibiting strange\n"
  338.                 "or obscure errors, consider this as suspect.\n"
  339.                 , nTexFiles);
  340.     }
  341.  
  342.     // sort texlumps in memory by name
  343.     qsort((void*)lumpinfo, (size_t) nTexLumps, sizeof(lumpinfo[0]), lump_sorter_by_name);
  344.  
  345.     SetKeyValue(&g_entities[0], "wad", szTmpWad);
  346.  
  347.     Log("\n");
  348.     CheckFatal();
  349.     return true;
  350. }
  351.  
  352. // =====================================================================================
  353. //  FindTexture
  354. // =====================================================================================
  355. lumpinfo_t*     FindTexture(const lumpinfo_t* const source)
  356. {
  357.     //Log("** PnFNFUNC: FindTexture\n");
  358.  
  359.     lumpinfo_t*     found = NULL;
  360.  
  361.     found = (lumpinfo_t*)bsearch(source, (void*)lumpinfo, (size_t) nTexLumps, sizeof(lumpinfo[0]), lump_sorter_by_name);
  362.     if (!found)
  363.     {
  364.         Warning("::FindTexture() texture %s not found!", source->name);
  365.         if (!strcmp(source->name, "NULL"))
  366.         {
  367.             Log("Are you sure you included zhlt.wad in your wadpath list?\n");
  368.         }
  369.     }
  370.  
  371.     return found;
  372. }
  373.  
  374. // =====================================================================================
  375. //  LoadLump
  376. // =====================================================================================
  377. int             LoadLump(const lumpinfo_t* const source, byte* dest, int* texsize)
  378. {
  379.     //Log("** PnFNFUNC: LoadLump\n");
  380.  
  381.     *texsize = 0;
  382.     if (source->filepos)
  383.     {
  384.         if (fseek(texfiles[source->iTexFile], source->filepos, SEEK_SET))
  385.         {
  386.             Warning("fseek to %d failed\n", source->filepos);
  387.         }
  388.         *texsize = source->disksize;
  389.  
  390.         bool wadinclude = false;
  391.         std::map< int, bool >::iterator it;
  392.         it = s_WadIncludeMap.find(source->iTexFile);
  393.         if (it != s_WadIncludeMap.end())
  394.         {
  395.             wadinclude = it->second;
  396.         }
  397.  
  398.         // Should we just load the texture header w/o the palette & bitmap?
  399.         if (g_wadtextures && !wadinclude)
  400.         {
  401.             // Just read the miptex header and zero out the data offsets.
  402.             // We will load the entire texture from the WAD at engine runtime
  403.             int             i;
  404.             miptex_t*       miptex = (miptex_t*)dest;
  405.             SafeRead(texfiles[source->iTexFile], dest, sizeof(miptex_t));
  406.  
  407.             for (i = 0; i < MIPLEVELS; i++)
  408.                 miptex->offsets[i] = 0;
  409.             return sizeof(miptex_t);
  410.         }
  411.         else
  412.         {
  413.             // Load the entire texture here so the BSP contains the texture
  414.             SafeRead(texfiles[source->iTexFile], dest, source->disksize);
  415.             return source->disksize;
  416.         }
  417.     }
  418.  
  419.     Warning("::LoadLump() texture %s not found!", source->name);
  420.     return 0;
  421. }
  422.  
  423. // =====================================================================================
  424. //  AddAnimatingTextures
  425. // =====================================================================================
  426. void            AddAnimatingTextures()
  427. {
  428.     int             base;
  429.     int             i, j, k;
  430.     char            name[MAXWADNAME];
  431.  
  432.     base = nummiptex;
  433.  
  434.     for (i = 0; i < base; i++)
  435.     {
  436.         if ((miptex[i].name[0] != '+') && (miptex[i].name[0] != '-'))
  437.         {
  438.             continue;
  439.         }
  440.  
  441.         safe_strncpy(name, miptex[i].name, MAXWADNAME);
  442.  
  443.         for (j = 0; j < 20; j++)
  444.         {
  445.             if (j < 10)
  446.             {
  447.                 name[1] = '0' + j;
  448.             }
  449.             else
  450.             {
  451.                 name[1] = 'A' + j - 10;                    // alternate animation
  452.             }
  453.  
  454.             // see if this name exists in the wadfile
  455.             for (k = 0; k < nTexLumps; k++)
  456.             {
  457.                 if (!strcmp(name, lumpinfo[k].name))
  458.                 {
  459.                     FindMiptex(name);                      // add to the miptex list
  460.                     break;
  461.                 }
  462.             }
  463.         }
  464.     }
  465.  
  466.     if (nummiptex - base)
  467.     {
  468.         Log("added %i additional animating textures.\n", nummiptex - base);
  469.     }
  470. }
  471.  
  472. // =====================================================================================
  473. //  GetWadPath
  474. // AJM: this sub is obsolete
  475. // =====================================================================================
  476. char*           GetWadPath()
  477. {
  478.     const char*     path = ValueForKey(&g_entities[0], "_wad");
  479.  
  480.     if (!path || !path[0])
  481.     {
  482.         path = ValueForKey(&g_entities[0], "wad");
  483.         if (!path || !path[0])
  484.         {
  485.             Warning("no wadfile specified");
  486.             return strdup("");
  487.         }
  488.     }
  489.    
  490.     return strdup(path);
  491. }
  492.  
  493. // =====================================================================================
  494. //  WriteMiptex
  495. // =====================================================================================
  496. void            WriteMiptex()
  497. {
  498.     int             len, texsize, totaltexsize = 0;
  499.     byte*           data;
  500.     dmiptexlump_t*  l;
  501.     double          start, end;
  502.  
  503.     g_texdatasize = 0;
  504.  
  505.     start = I_FloatTime();
  506.     {
  507.         if (!TEX_InitFromWad())
  508.             return;
  509.  
  510.         AddAnimatingTextures();
  511.     }
  512.     end = I_FloatTime();
  513.     Verbose("TEX_InitFromWad & AddAnimatingTextures elapsed time = %ldms\n", (long)(end - start));
  514.  
  515.     start = I_FloatTime();
  516.     {
  517.         int             i;
  518.  
  519.         for (i = 0; i < nummiptex; i++)
  520.         {
  521.             lumpinfo_t*     found;
  522.  
  523.             found = FindTexture(miptex + i);
  524.             if (found)
  525.             {
  526.                 miptex[i] = *found;
  527.             }
  528.             else
  529.             {
  530.                 miptex[i].iTexFile = miptex[i].filepos = miptex[i].disksize = 0;
  531.             }
  532.         }
  533.     }
  534.     end = I_FloatTime();
  535.     Verbose("FindTextures elapsed time = %ldms\n", (long)(end - start));
  536.  
  537.     start = I_FloatTime();
  538.     {
  539.         int             i;
  540.         texinfo_t*      tx = g_texinfo;
  541.  
  542.         // Sort them FIRST by wadfile and THEN by name for most efficient loading in the engine.
  543.         qsort((void*)miptex, (size_t) nummiptex, sizeof(miptex[0]), lump_sorter_by_wad_and_name);
  544.  
  545.         // Sleazy Hack 104 Pt 2 - After sorting the miptex array, reset the texinfos to point to the right miptexs
  546.         for (i = 0; i < g_numtexinfo; i++, tx++)
  547.         {
  548.             char*          miptex_name = texmap64_retrieve(tx->miptex);
  549.  
  550.             tx->miptex = FindMiptex(miptex_name);
  551.  
  552.             // Free up the originally strdup()'ed miptex_name
  553.             free(miptex_name);
  554.         }
  555.     }
  556.     end = I_FloatTime();
  557.     Verbose("qsort(miptex) elapsed time = %ldms\n", (long)(end - start));
  558.  
  559.     start = I_FloatTime();
  560.     {
  561.         int             i;
  562.  
  563.         // Now setup to get the miptex data (or just the headers if using -wadtextures) from the wadfile
  564.         l = (dmiptexlump_t*)g_dtexdata;
  565.         data = (byte*) & l->dataofs[nummiptex];
  566.         l->nummiptex = nummiptex;
  567.         for (i = 0; i < nummiptex; i++)
  568.         {
  569.             l->dataofs[i] = data - (byte*) l;
  570.             len = LoadLump(miptex + i, data, &texsize);
  571.  
  572.             if (!len)
  573.             {
  574.                 l->dataofs[i] = -1;                        // didn't find the texture
  575.             }
  576.             else
  577.             {
  578.                 totaltexsize += texsize;
  579.  
  580.                 hlassume(totaltexsize < g_max_map_miptex, assume_MAX_MAP_MIPTEX);
  581.             }
  582.             data += len;
  583.         }
  584.         g_texdatasize = data - g_dtexdata;
  585.     }
  586.     end = I_FloatTime();
  587.     Log("Texture usage is at %1.2f mb (of %1.2f mb MAX)\n", (float)totaltexsize / (1024 * 1024),
  588.         (float)g_max_map_miptex / (1024 * 1024));
  589.     Verbose("LoadLump() elapsed time = %ldms\n", (long)(end - start));
  590. }
  591.  
  592. //==========================================================================
  593.  
  594. // =====================================================================================
  595. //  TexinfoForBrushTexture
  596. // =====================================================================================
  597. int             TexinfoForBrushTexture(const plane_t* const plane, brush_texture_t* bt, const vec3_t origin)
  598. {
  599.     vec3_t          vecs[2];
  600.     int             sv, tv;
  601.     vec_t           ang, sinv, cosv;
  602.     vec_t           ns, nt;
  603.     texinfo_t       tx;
  604.     texinfo_t*      tc;
  605.     int             i, j, k;
  606.  
  607.     memset(&tx, 0, sizeof(tx));
  608.     tx.miptex = FindMiptex(bt->name);
  609.  
  610.     // Note: FindMiptex() still needs to be called here to add it to the global miptex array
  611.  
  612.     // Very Sleazy Hack 104 - since the tx.miptex index will be bogus after we sort the miptex array later
  613.     // Put the string name of the miptex in this "index" until after we are done sorting it in WriteMiptex().
  614.     tx.miptex = texmap64_store(strdup(bt->name));
  615.  
  616.     // set the special flag
  617.     if (bt->name[0] == '*'
  618.         || !strncasecmp(bt->name, "sky", 3)
  619.         || !strncasecmp(bt->name, "clip", 4)
  620.         || !strncasecmp(bt->name, "origin", 6) 
  621. #ifdef ZHLT_NULLTEX // AJM
  622.         || !strncasecmp(bt->name, "null", 4) 
  623. #endif
  624.         || !strncasecmp(bt->name, "aaatrigger", 10)
  625.        )
  626.     {
  627.         tx.flags |= TEX_SPECIAL;
  628.     }
  629.  
  630.     if (bt->txcommand)
  631.     {
  632.         memcpy(tx.vecs, bt->vects.quark.vects, sizeof(tx.vecs));
  633.         if (origin[0] || origin[1] || origin[2])
  634.         {
  635.             tx.vecs[0][3] += DotProduct(origin, tx.vecs[0]);
  636.             tx.vecs[1][3] += DotProduct(origin, tx.vecs[1]);
  637.         }
  638.     }
  639.     else
  640.     {
  641.         if (g_nMapFileVersion < 220)
  642.         {
  643.             TextureAxisFromPlane(plane, vecs[0], vecs[1]);
  644.         }
  645.  
  646.         if (!bt->vects.valve.scale[0])
  647.         {
  648.             bt->vects.valve.scale[0] = 1;
  649.         }
  650.         if (!bt->vects.valve.scale[1])
  651.         {
  652.             bt->vects.valve.scale[1] = 1;
  653.         }
  654.  
  655.         if (g_nMapFileVersion < 220)
  656.         {
  657.             // rotate axis
  658.             if (bt->vects.valve.rotate == 0)
  659.             {
  660.                 sinv = 0;
  661.                 cosv = 1;
  662.             }
  663.             else if (bt->vects.valve.rotate == 90)
  664.             {
  665.                 sinv = 1;
  666.                 cosv = 0;
  667.             }
  668.             else if (bt->vects.valve.rotate == 180)
  669.             {
  670.                 sinv = 0;
  671.                 cosv = -1;
  672.             }
  673.             else if (bt->vects.valve.rotate == 270)
  674.             {
  675.                 sinv = -1;
  676.                 cosv = 0;
  677.             }
  678.             else
  679.             {
  680.                 ang = bt->vects.valve.rotate / 180 * Q_PI;
  681.                 sinv = sin(ang);
  682.                 cosv = cos(ang);
  683.             }
  684.  
  685.             if (vecs[0][0])
  686.             {
  687.                 sv = 0;
  688.             }
  689.             else if (vecs[0][1])
  690.             {
  691.                 sv = 1;
  692.             }
  693.             else
  694.             {
  695.                 sv = 2;
  696.             }
  697.  
  698.             if (vecs[1][0])
  699.             {
  700.                 tv = 0;
  701.             }
  702.             else if (vecs[1][1])
  703.             {
  704.                 tv = 1;
  705.             }
  706.             else
  707.             {
  708.                 tv = 2;
  709.             }
  710.  
  711.             for (i = 0; i < 2; i++)
  712.             {
  713.                 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
  714.                 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
  715.                 vecs[i][sv] = ns;
  716.                 vecs[i][tv] = nt;
  717.             }
  718.  
  719.             for (i = 0; i < 2; i++)
  720.             {
  721.                 for (j = 0; j < 3; j++)
  722.                 {
  723.                     tx.vecs[i][j] = vecs[i][j] / bt->vects.valve.scale[i];
  724.                 }
  725.             }
  726.         }
  727.         else
  728.         {
  729.             vec_t           scale;
  730.  
  731.             scale = 1 / bt->vects.valve.scale[0];
  732.             VectorScale(bt->vects.valve.UAxis, scale, tx.vecs[0]);
  733.  
  734.             scale = 1 / bt->vects.valve.scale[1];
  735.             VectorScale(bt->vects.valve.VAxis, scale, tx.vecs[1]);
  736.         }
  737.  
  738.         tx.vecs[0][3] = bt->vects.valve.shift[0] + DotProduct(origin, tx.vecs[0]);
  739.         tx.vecs[1][3] = bt->vects.valve.shift[1] + DotProduct(origin, tx.vecs[1]);
  740.     }
  741.  
  742.     //
  743.     // find the g_texinfo
  744.     //
  745.     ThreadLock();
  746.     tc = g_texinfo;
  747.     for (i = 0; i < g_numtexinfo; i++, tc++)
  748.     {
  749.         // Sleazy hack 104, Pt 3 - Use strcmp on names to avoid dups
  750.         if (strcmp(texmap64_retrieve((tc->miptex)), texmap64_retrieve((tx.miptex))) != 0)
  751.         {
  752.             continue;
  753.         }
  754.         if (tc->flags != tx.flags)
  755.         {
  756.             continue;
  757.         }
  758.         for (j = 0; j < 2; j++)
  759.         {
  760.             for (k = 0; k < 4; k++)
  761.             {
  762.                 if (tc->vecs[j][k] != tx.vecs[j][k])
  763.                 {
  764.                     goto skip;
  765.                 }
  766.             }
  767.         }
  768.         ThreadUnlock();
  769.         return i;
  770. skip:;
  771.     }
  772.  
  773.     hlassume(g_numtexinfo < MAX_MAP_TEXINFO, assume_MAX_MAP_TEXINFO);
  774.  
  775.     *tc = tx;
  776.     g_numtexinfo++;
  777.     ThreadUnlock();
  778.     return i;
  779. }
  780.  
  781.